using System;
using System.Collections.Generic;
using MonoDevelop.Core;

namespace MonoDevelop.VersionControl
{
	public abstract class VersionControlSystem
	{
		/// <summary>
		/// Creates an instance of a repository for this version control system
		/// </summary>
		/// <returns>
		/// The repository instance.
		/// </returns>
		public Repository CreateRepositoryInstance ()
		{
			Repository rep = OnCreateRepositoryInstance ();
			rep.VersionControlSystem = this;
			return rep;
		}

		/// <summary>
		/// Identifier of the version control system
		/// </summary>
		/// <value>
		/// The identifier.
		/// </value>
		/// <remarks>
		/// This identifier is stored in configuration files, so it should not change.
		/// The default implementation returns the full name of the class.
		/// </remarks>
		public virtual string Id {
			get { return GetType ().ToString (); }
		}

		/// <summary>
		/// Display name of the version control system
		/// </summary>
		public abstract string Name { get; }

		/// <summary>
		/// The version of the native version control system compatible with this system.
		/// </summary>
		/// <value>The version string.</value>
		public virtual string Version {
			get { return "N/A"; }
		}

		/// <summary>
		/// Gets a value indicating whether this version control system is available
		/// </summary>
		/// <remarks>
		/// If the version control system depends on some native tools or libraries, this method should
		/// check if those dependencies are properly installed and return <c>false</c> if they are not.
		/// </remarks>
		public virtual bool IsInstalled {
			get { return false; }
		}

		/// <summary>
		/// Creates an instance of a repository for this version control system
		/// </summary>
		protected abstract Repository OnCreateRepositoryInstance ();

		/// <summary>
		/// Creates an editor object for a repository.
		/// </summary>
		/// <returns>
		/// The repository editor.
		/// </returns>
		/// <param name='repo'>
		/// A repository
		/// </param>
		public abstract IRepositoryEditor CreateRepositoryEditor (Repository repo);

		/// <summary>
		/// Gets a repository for a given local path and identifier
		/// </summary>
		/// <returns>
		/// The repository.
		/// </returns>
		/// <param name='path'>
		/// A local path
		/// </param>
		/// <param name='id'>
		/// An identifier. This identifier is generated by MD and normally identifies
		/// a project.
		/// </param>
		/// <remarks>
		/// If the local path belongs to a repository that has already returned
		/// in previous calls, the same repository instance should be returned
		/// to optimize memory and resource use. MonoDevelop keeps track of
		/// repository references and will Dispose the repository only when
		/// the last reference to the repo is freed.
		/// </remarks>
		public abstract Repository GetRepositoryReference (FilePath path, string id);

		/// <summary>
		/// Gets the repository path root under this version control system.
		/// </summary>
		/// <returns>The repository path.</returns>
		/// <param name="path">The path to start the repository detection from.</param>
		/// <param name="id">An identifier. This identifier is generated by MD and normally identifiers a project.</param>
		public FilePath GetRepositoryPath (FilePath path, string id)
		{
			return OnGetRepositoryPath (path, id);
		}

		/// <summary>
		/// Gets the repository path root under this version control system.
		/// </summary>
		/// <returns>The repository path.</returns>
		/// <param name="path">The path to start the repository detection from.</param>
		/// <param name="id">An identifier. This identifier is generated by MD and normally identifiers a project.</param>
		protected abstract FilePath OnGetRepositoryPath (FilePath path, string id);

		/// <summary>
		/// Gets the output directory path.
		/// </summary>
		/// <returns>Returns the relative path based on the remote path.</returns>
		/// <param name="remoteRelativePath">Remote Relative Path.</param>
		public virtual string GetRelativeCheckoutPathForRemote (string remoteRelativePath)
		{
			return remoteRelativePath.Replace ('/', System.IO.Path.DirectorySeparatorChar);
		}
	}

	public class CompareVersionControlSystem : IComparer<VersionControlSystem>
	{
		public int Compare (VersionControlSystem vcs1, VersionControlSystem vcs2)
		{
			int result;

			if (ReferenceEquals (vcs1, vcs2)) {
				result = 0;
			} else {
				if (vcs1 is null) {
					result = 1;
				} else if (vcs2 is null) {
					result = -1;
				} else {
					result = string.Compare(vcs1.Name, vcs2.Name, StringComparison.InvariantCultureIgnoreCase);
				}
			}

			return result;
		}
	}
}